package org.eclipse.iota.util;

import org.eclipse.iota.assignments.Assignment;
import org.eclipse.iota.model.Alternation;
import org.eclipse.iota.model.Clause;
import org.eclipse.iota.model.Constraint;
import org.eclipse.iota.model.Equation;
import org.eclipse.iota.model.Expression;
import org.eclipse.iota.model.Reference;
import org.eclipse.iota.model.Variable;
import org.eclipse.iota.visitor.AbstractIotaVisitor;
import org.eclipse.iota.visitor.IotaTraversal;

/**
 * An expression visitor that replaces occurrences of 
 * a variable by an expression.
 * @author Christian Krause
 * @generated NOT
 */
public class IotaVariableSubstituter extends AbstractIotaVisitor {
	
	// Variable assignment to be used.
	private Assignment assignment;
	
	// Excluded references.
	private Reference[] excluded;
	
	/**
	 * Constructor.
	 * @param assignment variable assignment.
	 * @param excluded Excluded references.
	 */
	public IotaVariableSubstituter(Assignment assignment, Reference... excluded) {
		this.assignment = assignment;
		this.excluded = excluded;
	}

	/**
	 * Convenience constructor.
	 * @param variable Variable to be replaced.
	 * @param template Template expression.
	 * @param excluded Excluded references.
	 */
	public IotaVariableSubstituter(Variable variable, Expression template, Reference... excluded) {
		assignment = new Assignment();
		assignment.put(variable, template);
		this.excluded = excluded;
	}
	
	public Assignment getAssignmnet() {
		return assignment;
	}
	
	@Override
	public void visit(Reference reference) {
		
		// Check if the variable is included in the assignment.
		Expression template = assignment.get(reference.getVariable());
		if (template==null) return;
		
		// Check if it should be excluded.
		for (int i=0; i<excluded.length; i++) {
			if (excluded[i]==reference) return;
		}
		
		// Do the substitution.
		Expression replacement = IotaCopyUtil.copy(template);
		IotaReplaceUtil.replace(reference, replacement);
	}
	
	/**
	 * Perform the replacement by starting the traversal.
	 * @param expression root expression.
	 */
	public void applyTo(Expression expression) {
		IotaTraversal.bottomUp(expression, this);
	}
	
	/**
	 * Perform the substitution on a constraint. 
	 * @param constraint Constraint.
	 */
	public void applyTo(Constraint constraint) {
		if (constraint instanceof Equation) {
			Equation equation = (Equation) constraint;
			if (equation.getLeft()!=null) applyTo(equation.getLeft());
			if (equation.getRight()!=null) applyTo(equation.getRight());
		}
	}
	
	/**
	 * Perform the substitution on a clause. 
	 * @param clause Clause.
	 */
	public void applyTo(Clause clause) {
		for (Constraint constraint : clause.getConstraints()) {
			applyTo(constraint);
		}
	}
	
	/**
	 * Perform the substitution on an alternation. 
	 * @param alternation Alternation.
	 */
	public void applyTo(Alternation alternation) {
		for (Clause clause : alternation.getClauses()) {
			applyTo(clause);
		}
	}
	
}
